﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using GameLibrary;
using GameLibrary.Unit;
using GameLibrary.Settlement;

namespace UserInterface.Views
{
    using UserControls;
    using Helpers;
    using Events;
    using Model;

    /// <summary>
    /// Interaction logic for PlayerMap.xaml
    /// </summary>
    public partial class PlayerMap : UserControl
    {
        #region variables

        private MapElement[,] MapElements;

        /// <summary>
        /// Get and set max grid column number
        /// </summary>
        internal int MaxGridColumns { get; set; }
        /// <summary>
        /// Get and set max grid row number 
        /// </summary>
        internal int MaxGridRows { get; set; }

        #region display

        internal IEnumerable<MapElement> DisplayedMapElements
        {
            get
            {
                return this.MapGrid.Children as IEnumerable<MapElement>;
            }
        }

        internal bool NeedReloadMapElement
        {
            get
            {
                return this.IsGridRowDefinitionNeedChange
                    || this.IsGridColumnDefinitionNeedChange;
            }
        }

        private bool IsGridColumnDefinitionNeedChange
        {
            get
            {
                if (this.MapGrid.Width <= this.MapGrid.MinWidth)
                    return false;

                if (Math.Abs(deltaWidth) > tileImageSize.Width
                    || this.MapGrid.ColumnDefinitions.Count != this.GetGridColumnDefinitionNumber()
                    )
                    return true;
                else
                    return false;
            }
        }

        private bool IsGridRowDefinitionNeedChange
        {
            get
            {
                if (this.MapGrid.Height <= this.MapGrid.MinHeight)
                    return false;
                if (Math.Abs(deltaHeight) > tileImageSize.Height
                    || this.MapGrid.RowDefinitions.Count != this.GetGridRowDefinitionNumber()
                    )
                    return true;
                else
                    return false;
            }
        }

        private double deltaHeight = 0;
        private double deltaWidth = 0;

        #region image

        private static BitmapImage cityImage;
        private static BitmapImage villageImage;
        private static BitmapImage unitImage;
        private static BitmapImage plainImage;
        private static BitmapImage hilleImage;
        private static BitmapImage mountainImage;

        private Size tileImageSize = new Size(128,128);

        private static ImageBrush cityImageBrush;
        private static ImageBrush villageImageBrush;
        private static ImageBrush unitImageBrush;
        private static ImageBrush plainImageBrush;
        private static ImageBrush hillImageBrush;
        private static ImageBrush mountainImageBrush;

        #endregion
        #endregion

        #endregion

        public PlayerMap(
            )
        {
            InitializeComponent();
        }

        internal void LoadImageBrushes(
            Uri city,
            Uri village,
            Uri unit,
            Uri plain,
            Uri hill,
            Uri mountain)
        {
            cityImage = new BitmapImage(city);
            villageImage = new BitmapImage(village);
            unitImage = new BitmapImage(unit);
            plainImage = new BitmapImage(plain);
            hilleImage = new BitmapImage(hill);
            mountainImage = new BitmapImage(mountain);

            cityImageBrush = new ImageBrush(cityImage);
            villageImageBrush = new ImageBrush(villageImage);
            unitImageBrush = new ImageBrush(unitImage);
            plainImageBrush = new ImageBrush(plainImage);
            hillImageBrush = new ImageBrush(hilleImage);
            mountainImageBrush = new ImageBrush(mountainImage);

            tileImageSize = new Size(mountainImage.Width, mountainImage.Height);
        }

        #region size

        private void SetMapGridSize(double height, double width)
        {
            this.MapGrid.Height = height;
            this.MapGrid.Width = width;
        }

        /// <summary>
        /// Get current grid row definition count
        /// </summary>
        internal int GridRowCount
        {
            get { return this.MapGrid.RowDefinitions.Count; }
        }

        /// <summary>
        /// Get current grid column definition count
        /// </summary>
        internal int GridColumnCount
        {
            get { return this.MapGrid.ColumnDefinitions.Count; }
        }

        #endregion

        #region move

        internal void Move(int moveHeight, int moveWidth)
        {
            this.deltaHeight += moveHeight;
            this.deltaWidth += moveWidth;
            this.GetNewGridDimension();
        }

        #endregion

        #region grid column and row definitions

        /// <summary>
        /// Reset grid column and row definitions
        /// </summary>
        internal void ResetGrid()
        {
            this.SetGridColumnDefinitions();
            this.SetGridRowDefinitions();
        }

        internal void SetMaxSize()
        {
          //  this.MaxHeight = this.MaxGridRows * this.tileImageSize.Height;
          //  this.MaxWidth = this.MaxGridColumns * this.tileImageSize.Width;
        }

        private void ResetGridPosition()
        {
            Canvas.SetTop(this.MapGrid, this.deltaHeight);
            Canvas.SetLeft(this.MapGrid, this.deltaWidth);
        }

        private void SetGridRowDefinitions()
        {
            int row = this.GetGridRowDefinitionNumber();
            this.MapGrid.RowDefinitions.Clear();
            for (int i = 0; i < row; i++)
                this.MapGrid.RowDefinitions.Add(new RowDefinition());
        }

        private void SetGridColumnDefinitions()
        {
            int column = this.GetGridColumnDefinitionNumber();
            this.MapGrid.ColumnDefinitions.Clear();
            for (int j = 0; j < column; j++)
                this.MapGrid.ColumnDefinitions.Add(new ColumnDefinition());
        }

        private void GetNewGridDimension()
        {
            if (this.IsGridColumnDefinitionNeedChange)
                this.SetGridColumnDefinitions();
            if (this.IsGridRowDefinitionNeedChange)
                this.SetGridRowDefinitions();

            if (NeedReloadMapElement == true)
                this.SetDeltaValuesToZero();

            this.ResetGridPosition();
        }

        private void SetDeltaValuesToZero()
        {
            this.deltaHeight = 0;
            this.deltaWidth = 0;
        }

        private int GetGridColumnDefinitionNumber()
        {
            int current = (int)Math.Ceiling(this.MapGrid.Width / tileImageSize.Width);
            if (current < this.MaxGridColumns)
                return current;
            else
                return this.MaxGridColumns;
        }

        private int GetGridRowDefinitionNumber()
        {
            int current = (int)Math.Ceiling(this.MapGrid.Height / tileImageSize.Height);
            if (current < this.MaxGridRows)
                return current;
            else
                return this.MaxGridRows;
        }

        #endregion

        #region load map elements

        internal void LoadMapElements(GameMapElement[,] gameMap)
        {
            this.MapElements = new MapElement[gameMap.GetLength(0), gameMap.GetLength(1)];
               
            for(int i=0;i<gameMap.GetLength(0);i++)
                for (int j = 0; j < gameMap.GetLength(1); j++)
                {
                    AddMapElementToPlayerMap(gameMap[i, j], i, j);
                }
        }

        private void AddMapElementToPlayerMap(GameMapElement element, int i, int j)
        {
            ImageBrush tile = GetTileImageBrush(element);
            ImageBrush settlement = GetSettlementImageBrush(element);
            ImageBrush unit = GetUnitImageBrush(element);

            MapElement me = new MapElement(tile, settlement, unit);
            AddContextMenuToMapElement(me, element);
            AddMapElementToGrid(me, i, j);
            this.MapElements[i, j] = me;
        }
        
        private ImageBrush GetTileImageBrush( GameMapElement element)
        {
            GameLibrary.Tile.TileType tile = element.Tile;
            if (tile == GameLibrary.Tile.TileHelper.Plain)
                return plainImageBrush;

            if (tile == GameLibrary.Tile.TileHelper.Mountain)
                return mountainImageBrush;

            if (tile == GameLibrary.Tile.TileHelper.Hill)
                return hillImageBrush;

            throw new ArgumentException("Tile type not exist");
        }

        private ImageBrush GetUnitImageBrush(GameMapElement element)
        {
            if (element.Units.Count() > 0)
                return unitImageBrush;
            else
                return null;
        }

        private ImageBrush GetSettlementImageBrush(GameMapElement element)
        {
            if (element.Settlement != null)
                if (element.Settlement.Type == SettlementFlags.City)
                    return cityImageBrush;
                else
                    if (element.Settlement.Type == SettlementFlags.Village)
                        return villageImageBrush;
            return null;
        }

        private void AddContextMenuToMapElement(MapElement me, GameMapElement element)
        {
            SetTileMenu(me, element.Tile);
            SetSettlementMenu(me, element.Settlement);
            SetUnitMenu(me, element.Units);
        }

        private void SetTileMenu(MapElement me, GameLibrary.Tile.TileType tile)
        {
            ContextMenu tileMenu = ContextMenuCreator
                                    .GetContextMenuForTile(tile);
            me.SetTileContextMenu(tileMenu);
        }
        private void SetSettlementMenu(MapElement me, 
                                       GameLibrary.Settlement.SettlementInfo settlement)
        {
            if (settlement != null)
            {
                ContextMenu settlementMenu = ContextMenuCreator
                                            .GetContextMenu(settlement);
                me.SetSettlementContextMenu(settlementMenu);
            }
        }
        private void SetUnitMenu(MapElement me,
                                IEnumerable<GameLibrary.Unit.UnitInfo> units)
        {
            if (units.Count() > 0)
            {
                ContextMenu unitMenu;
                if (units.Count() == 1)
                {
                    unitMenu = ContextMenuCreator
                              .GetContextMenu(units.First());
                }
                else
                    unitMenu = ContextMenuCreator
                              .GetContextMenuForItems(units);
                me.SetUnitContextMenu(unitMenu);
            }
        }
        private void AddMapElementToGrid(MapElement me, int x, int y)
        {
            Grid.SetColumn(me, x);
            Grid.SetRow(me, y);

            this.MapGrid.Children.Add(me);
        }

        #region code safe to delete
        /* 
            BitmapImage bi = new BitmapImage(
                new Uri(@"../../Resources/Icons/GameObjects/Plain.jpg", UriKind.Relative));
            BitmapImage biu = new BitmapImage(
                        new Uri(@"../../Resources/Icons/GameObjects/Unit.jpg", UriKind.Relative));
            BitmapImage bic = new BitmapImage(
                    new Uri(@"../../Resources/Icons/GameObjects/City.jpg", UriKind.Relative));
            ImageBrush imageBrush = new ImageBrush(bi);
            ImageBrush ibu = new ImageBrush(biu);
            ImageBrush ibc = new ImageBrush(bic);

            GameLibrary.Settlement.SettlementInfo si = new GameLibrary.Settlement.SettlementInfo();
            GameLibrary.Unit.UnitInfo ui = new GameLibrary.Unit.UnitInfo();

            this.MapElements = new MapElement[this.MapGrid.ColumnDefinitions.Count,
                                               this.MapGrid.RowDefinitions.Count
                                               ];

            for (int i = 0; i < this.MapGrid.RowDefinitions.Count; i++)
                for (int j = 0; j < this.MapGrid.ColumnDefinitions.Count; j++)
                {
                    MapElement me = new MapElement(imageBrush, ibc, ibu);
                   me.SetSettlementContextMenu(ContextMenuCreator.GetContextMenu(si));
                   me.SetUnitContextMenu(ContextMenuCreator.GetContextMenu(ui));
                   me.SetTileContextMenu(ContextMenuCreator.GetContextMenuForTile(GameLibrary.Tile.TileHelper.Plain));
                    Grid.SetColumn(me, j);
                    Grid.SetRow(me, i);
                    this.MapGrid.Children.Add(me);
                    MapElements[j, i] = me;
                }
        }
        */
        #endregion

        #endregion

        #region events

        private void DownButton_Clicked(object sender, RoutedEventArgs e)
        {
            this.OnKeyPressed(new Position(1, 0));
        }

        private void UpButton_Clicked(object sender, RoutedEventArgs e)
        {
            this.OnKeyPressed(new Position(-1, 0));
        }

        private void RightButton_Clicked(object sender, RoutedEventArgs e)
        {
            this.OnKeyPressed(new Position(0, 1));
        }

        private void LeftButton_Clicked(object sender, RoutedEventArgs e)
        {
            this.OnKeyPressed(new Position(0, -1));
        }

        private void MapHelper_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            this.SetMapGridSize(e.NewSize.Height, e.NewSize.Width);
            this.GetNewGridDimension();
        }

        private void MapGrid_ButtonClick(object sender, RoutedEventArgs e)
        {
            this.OnMouseLeftButtonClickButton(e);
        }

        private void MapGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (e.Source is MapElement)
            {
                this.OnMouseLeftButtonClickGrid(e);
                e.Handled = true;
            }
        }

        public event EventHandler<KeyPressedEventArgs> KeyPressed;

        private void OnKeyPressed(Position pos)
        {
            if (KeyPressed != null)
                this.KeyPressed(this, new KeyPressedEventArgs(pos));
        }

        public event EventHandler<MouseLeftButtonClickGridEventArgs> LeftButtonClickGrid;
        public event EventHandler<MouseLeftButtonClickButtonEventArgs> LeftButtonClickButton;

        private void OnMouseLeftButtonClickGrid(MouseButtonEventArgs mouseEvent)
        {
            if (this.LeftButtonClickGrid != null)
            {
                MapElement me = this.GetMapElementFromEventSource(mouseEvent.Source);
                int mex, mey;
                this.GetMapElementGridPosition(me, out mex, out mey);
                this.LeftButtonClickGrid(
                    this, new MouseLeftButtonClickGridEventArgs(mex, mey)
                    );
            }
        }

        private void OnMouseLeftButtonClickButton(RoutedEventArgs e)
        {
            if (this.LeftButtonClickButton != null)
            {
                MapElement me = this.GetMapElementFromEventSource(e.Source);
                ObjectOnMap obj = this.GetObjectOnMapFromEventOriginalSource(e.OriginalSource);
                int meX, meY, objX, objY;
                this.GetMapElementGridPosition(me, out meX, out meY);
                this.GetObjectOnMapGridPosition(obj, out objX, out objY);
                this.LeftButtonClickButton(
                    this,
                    new MouseLeftButtonClickButtonEventArgs(
                        meX, meY, objX, objY)
                        );
            }
        }

        private MapElement GetMapElementFromEventSource(object sender)
        {
            if (sender is MapElement)
                return sender as MapElement;
            else
                throw new ArgumentException("source is not map element.");
        }

        private ObjectOnMap GetObjectOnMapFromEventOriginalSource(object source)
        {
            if (source is Button)
            {
                var button = source as Button;
                var border = button.Parent as Border;
                var obj = border.Parent as ObjectOnMap;
                return obj;
            }
            else
                throw new ArgumentException("source is not button");
        }

        private void GetMapElementGridPosition(MapElement me, out int x, out int y)
        {
            x = Grid.GetColumn(me);
            y = Grid.GetRow(me);
        }

        private void GetObjectOnMapGridPosition(ObjectOnMap obj, out int x, out int y)
        {
            x = Grid.GetColumn(obj);
            y = Grid.GetRow(obj);
        }

        #endregion

        #region expander
        internal void ShowInfoInExpander(UserControl view)
        {
            Information.Content = view;
            Information.IsExpanded = true;
        }
        #endregion

        internal void ChangeGridMapElementColor(IEnumerable<Position> elementList, Presenters.CommandType command =  Presenters.CommandType.None)
        {
            SolidColorBrush brush = GetBrushFromCommand(command);
            foreach (Position pos in elementList)
            {
                MapElements[pos.X, pos.Y].ContentGrid.Background = brush;
                MapElements[pos.X, pos.Y].ContentGrid.Opacity = 0.5;
            }
        }

        private SolidColorBrush GetBrushFromCommand(Presenters.CommandType command)
        {
            switch (command)
            {
                case Presenters.CommandType.UnitAttack:
                    return Brushes.OrangeRed;
                case Presenters.CommandType.UnitMove:
                    return Brushes.LightGreen;
                case Presenters.CommandType.UnitExplore:
                    return Brushes.Blue;
                default:
                    return null;
            }
        }

        #region remainer from test, safe to delete

        // for test
        private void ShowMouseEventInMessageBox(MouseButtonEventArgs e)
        {            
            MessageBox.Show(
                "SourceType: " + e.Source.GetType().ToString()
                +"Source Position in MapGrid\n"
                + " width: " + Grid.GetColumn(e.Source as MapElement)
                + " height: " + Grid.GetRow(e.Source as MapElement)
                + "\nOrg source type: " + e.OriginalSource.GetType().ToString()
                );
        }

        // for test safe to delete
        private void ShowButtonClickEvent(object sender, RoutedEventArgs e)
        {
            //int x, y;
            var me = e.Source as MapElement;
            var bu = e.OriginalSource as Button;
            var border = bu.Parent as Border;
            var objonmap = border.Parent as ObjectOnMap;

            MessageBox.Show(
                "Sender type: "
                + sender.GetType().ToString()
                + "\n source : "
                + e.Source.GetType().ToString()
                + "\n position in map grid"
                + Grid.GetColumn(e.Source as UIElement) + " " + Grid.GetRow(e.Source as MapElement)
                + "\n origin source : "
                + e.OriginalSource.GetType().ToString()
                + "\n position in map element"
                + Grid.GetColumn(objonmap) + " " + Grid.GetRow(objonmap)
                );

        }

        #endregion

        internal void SetNextTurnVisible()
        {
            this.NextTurn.Visibility = System.Windows.Visibility.Visible;
        }

    }
}
